#include "grflib_stdafx.h"
#include "DXUTgui.h"
#include "SDKmisc.h"
#include "../include/Effect.h"
#include <D3Dcompiler.h>
#include "../include/Hash.h"
#include "../include/D3D10Engine.h"
#include "../include/Material.h"

using namespace grflib::engine::core;

CEffect::CEffect()
	: m_pEffect(0)
{
}

CEffect::~CEffect()
{
	OnDestroy();
}

//Call this first before inherited create method
HRESULT CEffect::OnCreateDevice(ID3D10Device *pd3dDevice,
								const DXGI_SURFACE_DESC *pBufferSurfaceDesc)
{
	if (m_filename.length() == 0)
		return S_OK;

	HRESULT hr;
    DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
	#if defined( DEBUG ) || defined( _DEBUG )
    dwShaderFlags |= D3D10_SHADER_DEBUG;
    #endif

	std::string filename;
	char szFilename[MAX_PATH];
	w32lib::WCharToChar(szFilename, m_filename.c_str());
	_SearchPaths.Complete(szFilename, filename);
	V_RETURN( D3DX10CreateEffectFromFileA(filename.c_str(), NULL, NULL, "fx_4_0", dwShaderFlags, 0, pd3dDevice, NULL,
										 NULL, &m_pEffect, NULL, NULL) );

	/////////////////////////////////////////////
	//hashing shader signatures
	D3D10_EFFECT_DESC effectDesc;
	D3D10_TECHNIQUE_DESC techDesc;
	D3D10_PASS_DESC passDesc;
	//D3D10_SHADER_DESC shaderDesc;
	D3D10_PASS_SHADER_DESC passShaderDesc;
	D3D10_EFFECT_SHADER_DESC effectShaderDesc;
	D3D10_SIGNATURE_PARAMETER_DESC signatureDesc;
	ID3D10EffectPass *pPass;

	//get maximum technique/pass number
	UINT nTech;
	V_RETURN( m_pEffect->GetDesc(&effectDesc) );
	nTech = effectDesc.Techniques;

	//hashing every pass
	std::size_t vertexShaderSignature;

	for (UINT i = 0; i < nTech; i++)
	{
		V_RETURN( m_pEffect->GetTechniqueByIndex(i)->GetDesc(&techDesc) );
		for (UINT j = 0; j < techDesc.Passes; j++)
		{
			pPass = m_pEffect->GetTechniqueByIndex(i)->GetPassByIndex(j);
			V_RETURN( pPass->GetDesc(&passDesc) );
			V_RETURN( pPass->GetVertexShaderDesc(&passShaderDesc) );
			V_RETURN( passShaderDesc.pShaderVariable->GetShaderDesc(passShaderDesc.ShaderIndex, &effectShaderDesc) );

			vertexShaderSignature = 0; //reset signature hash seed for each shader
			for (UINT k = 0; k < effectShaderDesc.NumInputSignatureEntries; k++)
			{
				V_RETURN( passShaderDesc.pShaderVariable->GetInputSignatureElementDesc(passShaderDesc.ShaderIndex, k, &signatureDesc) );
				boost::hash_combine(vertexShaderSignature, signatureDesc);
			}

			m_VSSignatures[pPass] = vertexShaderSignature;
		}
	}
	/////////////////////////////////////////////

	/////////////////////////////////////////////
	//retrieve techniques
	for (technique_iter iter = m_pTechniques.begin(); iter != m_pTechniques.end(); ++iter)
	{
		iter->second = m_pEffect->GetTechniqueByName(iter->first.c_str());
	}

	/////////////////////////////////////////////
	//retrieve effect variables
	for (shader_var_iter iter = m_pShaderVars.begin(); iter != m_pShaderVars.end(); ++iter)
	{
		switch (iter->second.second)
		{
		case SVUSE_MATRIX:
			iter->second.first = m_pEffect->GetVariableByName(iter->first.c_str())->AsMatrix();
			break;
		case SVUSE_SHADER_RESOURCE:
			iter->second.first = m_pEffect->GetVariableByName(iter->first.c_str())->AsShaderResource();
			break;
		case SVUSE_VECTOR:
			iter->second.first = m_pEffect->GetVariableByName(iter->first.c_str())->AsVector();
			break;
		case SVUSE_SCALAR:
			iter->second.first = m_pEffect->GetVariableByName(iter->first.c_str())->AsScalar();
			break;
		default:
			break;
		}
	}

	return S_OK;
}

void CEffect::OnDestroy()
{
	SAFE_RELEASE(m_pEffect);
	m_VSSignatures.clear();
}

ID3D10EffectVariable * CEffect::GetShaderVariable(const std::string &name)
{
	shader_var_iter iter;
	if ((iter = m_pShaderVars.find(name)) != m_pShaderVars.end())
	{
		return iter->second.first;
	}
	return 0;
}

ID3D10EffectTechnique * CEffect::GetTechnique(const std::string &name)
{
	technique_iter iter;
	if ((iter = m_pTechniques.find(name)) != m_pTechniques.end())
	{
		return iter->second;
	}
	return 0;
}

std::size_t CEffect::GetVSSignature(ID3D10EffectPass *pPass)
{
	boost::unordered_map<ID3D10EffectPass *, std::size_t>::iterator iter;
	if ((iter = m_VSSignatures.find(pPass)) != m_VSSignatures.end())
	{
		return iter->second;
	}
	return 0;
}

void CEffect::AddShaderVariable(const std::string &name, SVUsage usage)
{
	m_pShaderVars[name] = std::pair<ID3D10EffectVariable *, SVUsage>((ID3D10EffectVariable *)0, usage);
}

void CEffect::AddTechnique(const std::string &name)
{
	m_pTechniques[name] = (ID3D10EffectTechnique *)0;
}

HRESULT CEffect::SetShaderVariables(boost::shared_ptr<CMaterial> &pMaterial, const std::string &strTechnique)
{
	return S_OK;
}

